package com.fingard.csp.service.socket.impl;

import com.fingard.csp.common.date.Format;
import com.fingard.csp.common.log.LogHelper;
import com.fingard.csp.common.text.StringHelper;
import com.fingard.csp.common.xml.XMLUtils;
import com.fingard.csp.domain.simulate.SimulateRule;
import com.fingard.csp.domain.simulate.SimulateRuleDetail;
import com.fingard.csp.mapper.datasourcefirst.simulate.SimulateRuleDetailMapper;
import com.fingard.csp.mapper.datasourcefirst.simulate.SimulateRuleMapper;
import com.fingard.csp.service.socket.SocketMessageService;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * Socket客户端消息处理服务实现类
 *
 * @author fanghh
 * @date 2021.07.20
 * @version v1.0.1
 */
@Slf4j
@Service
public class SocketMessageServiceImpl implements SocketMessageService {
    @Autowired
    private SimulateRuleMapper simulateRuleMapper;

    @Autowired
    private SimulateRuleDetailMapper simulateRuleDetailMapper;

    /**
     * 根据收到的客户端信息内容关键字段来识别是哪种来源报文，并针对该种报文进行业务处理并返回服务端响应报文
     *
     * @param params
     * @return
     * @throws Exception
     */
    @Override
    public String dealRequest(String params) throws Exception {
        String ruleMsgType = "";

        String bankCode = "";
        if (params.contains("<ap>") && (params.contains("<CCTransCode>") || params.contains("<TransCode>"))) {
            // 客户端消息为农行请求数据
            bankCode = "ABC02";
        } else if (params.contains("<CMBSDKPGK>") && params.contains("<FUNNAM>")) {
            // 客户端消息为招行请求数据
            bankCode = "CMB01";
        } else if (params.contains("<root>") && params.contains("<OpName>")) {
            // 客户端消息为邮储请求数据
            bankCode = "PSBC01";
        } else if (params.contains("<ap>") && params.contains("<tr_code>")) {
            // 客户端消息为交行请求数据
            bankCode = "BOCOM01";
        } else if ((params.contains("<CMS>") && params.contains("<TransCode>")) || params.contains("&TransCode=") && params.contains("&BankCode=102")) {
            // 客户端消息为工行请求数据
            bankCode = "ICBC02";
            if (params.contains("&TransCode=") && params.contains("&BankCode=102")) {
                ruleMsgType = "0";
            }
        } else if (params.contains("<Root>") && params.contains("<CorpBankCode>")) {
            // 客户端消息为融汇请求数据
            bankCode = "FnGrd01";
        }

        Map<String, List<SimulateRuleDetail>> mapSimulateRuleDetail = findSimulateRuleDetailList(bankCode);
        SimulateRule simulateRule = matchRules(params, mapSimulateRuleDetail);

        String tmpRespBody = "";
        if (StringHelper.hasAnyChar(simulateRule.getUrid())) {
            log.info("匹配规则结果成功,ruleid:[" + simulateRule.getUrid()+"].");
            SimulateRule tmpSimulateResult = simulateRuleMapper.findByUrid(simulateRule);
            tmpRespBody = tmpSimulateResult.getRuleresult();
        } else {
            log.info("匹配规则结果失败,暂无ruleid!");
            tmpRespBody = simulateRule.getRuleresult();
        }

        String responseMessage = "";
        if (StringHelper.hasAnyChar(tmpRespBody)) {
            if ("ABC02".equalsIgnoreCase(bankCode)) {
                String tmpRespHead = "0" + String.format("%-6d", tmpRespBody.getBytes("gbk").length);
                responseMessage = tmpRespHead + tmpRespBody;
            } else if ("CMB01".equalsIgnoreCase(bankCode)) {
                String tmpRespHead = String.format("%-8d", tmpRespBody.getBytes("gbk").length);
                responseMessage = tmpRespHead + tmpRespBody;
            } else if ("PSBC01".equalsIgnoreCase(simulateRule.getRuleresult())) {
                String tmpRespHead = String.format("%5s", tmpRespBody.getBytes("gbk").length).replace(' ', '0') + "00000";
                responseMessage = tmpRespHead + tmpRespBody;
            } else if ("BOCOM01".equalsIgnoreCase(simulateRule.getRuleresult())) {
                String tmpRespHead = "0" + String.format("%-6d", tmpRespBody.getBytes("gbk").length);
                responseMessage = tmpRespHead + tmpRespBody;
            } else if ("ICBC02".equalsIgnoreCase(simulateRule.getRuleresult())) {
                StringBuilder sb = new StringBuilder();
                sb.append("POST / HTTP/1.1\r\n");
                sb.append("Content-Type: INFOSEC_SIGN/1.0\r\n");
                sb.append("Host: " + "127.0.0.1" + ":" + "9696" + "\r\n");
                sb.append("Connection: close\r\n");

                if ("1".equalsIgnoreCase(ruleMsgType)) {
                    sb.append("Content-Length: " + tmpRespBody.getBytes("gbk").length + "\r\n");
                    sb.append("\r\n");
                    sb.append(tmpRespBody);
                } else {
                    String body = "Version=0.0.0.1&TransCode=PAYENT&BankCode=102&GroupCIS=390190001501002&ID=test122.y.3901&PackageID=525163414957&Cert=&reqData=" + tmpRespBody;
                    sb.append("Content-Length: " + body.getBytes("gbk").length + "\r\n");
                    sb.append("\r\n");
                    sb.append(body);
                }
                responseMessage = sb.toString();
            } else if ("FnGrd01".equalsIgnoreCase(bankCode)) {
                String tmpStrLen = String.format("% 8d", tmpRespBody.getBytes("gbk").length);
                String tmpRespHead = "Content-Length:" + tmpStrLen + "\r\n";
                responseMessage = tmpRespHead + tmpRespBody;
            }
        }

        return responseMessage;
    }

    /**
     * 查找签约规则列表(按照线路、ruleid遍历查找模拟规则并分组封装返回)<br/><br/>
     *
     * 业务实现过程：<br/>
     * 1、请求参数为线路代码,根据线路代码遍历t_simulaterule表得到结果；<br/>
     * 2、在查到当前线路的签约规则记录后再根据groupid进行分组得到groupid数组；<br/>
     * 3、循环遍历groupid数组，根据groupid获取当前分组的签约规则并存入list集合中；<br/>
     *    存入成功后再在外层用map对当前list集合进行封装,map的key是groupid,value是当前groupid对应的规则组<br/>
     *
     * @param bankCode 线路代码
     * @return 签约规则
     */
    public Map<String, List<SimulateRuleDetail>> findSimulateRuleDetailList(String bankCode) throws Exception {
        Map<String, List<SimulateRuleDetail>> mapSimulateRuleDetail = new HashMap<String, List<SimulateRuleDetail>>();
        SimulateRule simulateRule = new SimulateRule();
        simulateRule.setBankcode(bankCode);
        List<String> ruleGroupList = simulateRuleMapper.findRuleGroup(simulateRule);
        Iterator<String> ruleGroupIterator = ruleGroupList.iterator();
        while(ruleGroupIterator.hasNext()) {
            String ruleId = ruleGroupIterator.next();
            SimulateRuleDetail simulateRuleDetail = new SimulateRuleDetail();
            simulateRuleDetail.setRuleid(ruleId);
            List<SimulateRuleDetail> simulateRuleDetailList = simulateRuleDetailMapper.findByRuleId(simulateRuleDetail);
            mapSimulateRuleDetail.put(ruleId, simulateRuleDetailList);
        }
        return mapSimulateRuleDetail;
    }

    /**
     * 根据来源报文、模拟规则进行匹配<br/><br/>
     *
     * 业务实现过程：<br/>
     * 1、遍历当前模拟规则map，依据ruleid作为分组拆分；<br/>
     * 2、在当前ruleid所属规则分组中遍历每一个单独的规则：<br/>
     *    模拟规则如下：<br/>
     *    1、当模拟规则key是'Contains'时，表示请求报文必须包含key对应value中数组中的其中一个;<br/>
     *    2、当模拟规则value是固定值时，表示请求报文必须有key对应的节点，并且报文节点值必须和value一致;<br/>
     *    3、当模拟规则value是 %'字符串'时，表示请求报文必须有key对应的节点，并且报文节点值必须是'字符串'对应值结尾；比如 "%00"表示必须是00结尾的才匹配;<br/>
     *    4、当模拟规则value是%'字符串'%时，表示请求报文必须有key对应的节点，并且报文节点值必须包含'字符串'对应值;   比如 "%00%"表示字段值只要包含00即可;<br/>
     *    5、当模拟规则value是'字符串'%时，表示请求报文必须有key对应的节点，并且报文节点值必须是'字符串'对应值开始;   比如 "00%"表示字段值必须是00开头的开匹配;<br/>
     * 3、当当前这一组ruleid的规则均匹配时，会直接返回匹配成功，并开始读取当前模拟规则对应的响应结果(取t_simulate_rule_detail表的结果)<br/>
     *    当当前这一组ruleid的规则至少有一个不匹配时，则进入下一个循环继续匹配；<br/>
     *    当所有规则均匹配失败时，则返回统一的错误响应给到DSP；<br/>
     *
     * @param params 来源报文
     * @param mapSimulateRules 模拟规则
     * @return 规则结果
     */
    public SimulateRule matchRules(String params, Map<String, List<SimulateRuleDetail>> mapSimulateRules) {
        SimulateRule tmpRespSimulateRule = new SimulateRule();

        try {
            Document messageDocument = DocumentHelper.parseText(params);

            boolean isMatched = true;
            Iterator<String> iterator = mapSimulateRules.keySet().iterator();
            while (iterator.hasNext()) {
                isMatched = true;
                String ruleId = iterator.next();
                log.info("----------------------------------------------开始匹配当前规则"+ruleId+"，规则ID:[" + ruleId + "]----------------------------------------------");
                List<SimulateRuleDetail> simulateRuleDetails = mapSimulateRules.get(ruleId);
                Iterator<SimulateRuleDetail> simulateRuleDetailIterator = simulateRuleDetails.iterator();
                while (simulateRuleDetailIterator.hasNext()) {
                    SimulateRuleDetail simulateRuleDetail = simulateRuleDetailIterator.next();
                    String ruleKey = simulateRuleDetail.getRulekey();
                    String ruleValue = simulateRuleDetail.getRulevalue();
                    ruleValue = ruleValue.replace("${TODAY}", Format.Date8Format.format(new Date()));
                    log.info("当前规则明细参数信息:[规则明细参数-("+ruleKey+ "),规则明细参数值-("+ ruleValue+ ")]");

                    if ("Contains".equalsIgnoreCase(ruleKey)) {
                        log.info("当前匹配规则为Contains-包含,开始进行包含判断...");
                        boolean isContained = false;
                        for(String sourceNote : ruleValue.split(",")) {
                            log.info("当前规则匹配字段名:(" + sourceNote + ")");
                            isContained = new XMLUtils(messageDocument).containName(sourceNote);
                            if (isContained) {
                                log.info("来源系统匹配字段名已包含-当前字段"+sourceNote+"包含校验成功,进行下一包含匹配字段名的包含匹配...\r\n");
                            }
                        }
                        if (!isContained) {
                            isMatched = false;
                            log.info("来源系统匹配字段名["+ruleValue+"]均未出现,包含校验失败,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                            break;
                        }
                    } else if ("IsParity".equalsIgnoreCase(ruleValue)) {
                        log.info("当前匹配规则为IsParity-使用奇偶,作用字段:" + ruleKey);
                        String targetRuleValue = new XMLUtils(messageDocument).getNodeValue(ruleKey);
                        log.info("当前匹配规则字段:" + ruleKey + "的值:" + targetRuleValue);
                        int ruleNum = Integer.parseInt(targetRuleValue.substring(targetRuleValue.length() - 1));
                        log.info("当前匹配规则字段:" + ruleKey + "的尾号数字值:" + ruleNum);
                        if (ruleNum % 2 != 1) {
                            isMatched = false;
                            log.info("当前匹配规则字段:" + ruleKey + "的尾号数字值:" + ruleNum + "不是奇数,校验失败,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                            break;
                        } else {
                            log.info("当前匹配规则字段:" + ruleKey + "的尾号数字值:" + ruleNum + "是奇数,校验成功,进行下一包含匹配字段名的包含匹配...\r\n");
                        }
                    } else if ("NonParity".equalsIgnoreCase(ruleValue)) {
                        log.info("当前匹配规则为NonParity-使用偶数,作用字段:" + ruleKey);
                        String targetRuleValue = new XMLUtils(messageDocument).getNodeValue(ruleKey);
                        log.info("当前匹配规则字段:" + ruleKey + "的值:" + targetRuleValue);
                        int ruleNum = Integer.parseInt(targetRuleValue.substring(targetRuleValue.length() - 1));
                        log.info("当前匹配规则字段:" + ruleKey + "的尾号数字值:" + ruleNum);
                        if (ruleNum % 2 != 0) {
                            isMatched = false;
                            log.info("当前匹配规则字段:" + ruleKey + "的尾号数字值:" + ruleNum + "不是偶数,校验失败,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                            break;
                        } else {
                            log.info("当前匹配规则字段:" + ruleKey + "的尾号数字值:" + ruleNum + "是偶数,校验成功,进行下一包含匹配字段名的包含匹配...\r\n");
                        }
                    } else if (ruleValue.contains("%")) {
                        int countChar = StringHelper.countChar(ruleValue, '%');
                        if (countChar == 2) {
                            // 两个%
                            int startIndex = ruleValue.indexOf("%");
                            int endIndex = ruleValue.lastIndexOf("%");
                            String matchStr = ruleValue.substring(startIndex + 1, endIndex);
                            log.info("当前规则匹配字段名模糊匹配值:(" + matchStr + ")");

                            XMLUtils document = new XMLUtils(messageDocument);
                            if (document.containName(ruleKey)) {
                                log.info("来源系统匹配字段名实际值:(" + new XMLUtils(messageDocument).getNodeValue(ruleKey) + ")\r\n");
                                if (!new XMLUtils(messageDocument).getNodeValue(ruleKey).contains(matchStr)) {
                                    isMatched = false;
                                    log.info("当前规则匹配字段名模糊匹配不一致,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                                    break;
                                }
                            } else {
                                log.info("来源系统匹配字段名不存在:(" + ruleKey + ")\r\n");
                                isMatched = false;
                                log.info("当前规则匹配字段名模糊匹配不一致,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                                break;
                            }
                        } else if (countChar == 1) {
                            // 一个%
                            String matchStr = ruleValue.substring(ruleValue.indexOf("%") + 1);
                            log.info("当前规则匹配字段名模糊匹配值:(" + matchStr + ")");
                            XMLUtils document = new XMLUtils(messageDocument);
                            if (document.containName(ruleKey)) {
                                log.info("来源系统匹配字段名实际值:(" + new XMLUtils(messageDocument).getNodeValue(ruleKey) + ")\r\n");
                                if (!document.getNodeValue(ruleKey).endsWith(matchStr)) {
                                    isMatched = false;
                                    log.info("当前规则匹配字段名模糊匹配不一致,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                                    break;
                                }
                            } else {
                                log.info("来源系统匹配字段名不存在:(" + ruleKey + ")\r\n");
                                isMatched = false;
                                log.info("当前规则匹配字段名模糊匹配不一致,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                                break;
                            }
                        }
                    } else {
                        log.info("当前规则匹配字段名对应值:(" + ruleValue + ")");

                        XMLUtils document = new XMLUtils(messageDocument);
                        if (document.containName(ruleKey)) {
                            log.info("来源系统匹配字段名对应值:(" + new XMLUtils(messageDocument).getNodeValue(ruleKey) + ")\r\n");
                            if (!ruleValue.equalsIgnoreCase(new XMLUtils(messageDocument).getNodeValue(ruleKey))) {
                                isMatched = false;
                                log.info(" 当前规则匹配字段名匹配不一致,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                                break;
                            }
                        } else {
                            log.info("来源系统匹配字段名不存在:(" + ruleKey + ")\r\n");
                            isMatched = false;
                            log.info("当前规则匹配字段名模糊匹配不一致,断开当前匹配规则,开始进入下一匹配规则...\r\n");
                            break;
                        }
                    }
                }

                if (isMatched) {
                    // 匹配成功
                    log.info("------------------------------------------------当前规则匹配成功,开始返回-------------------------------------------------");
                    tmpRespSimulateRule.setUrid(ruleId);
                    break;
                }
            }

            if (!isMatched) {
                // 所有规则均匹配失败
                log.info("----------------------------------------------所有规则匹配均失败,开始返回统一错误信息----------------------------------------------");
                tmpRespSimulateRule.setRuleresult("所有规则均匹配失败!");
            }
        } catch (Exception ex) {
            log.info("----------------------------------------------模拟规则匹配失败,原因:" + LogHelper.getStackTrace(ex)+"----------------------------------------------");
            tmpRespSimulateRule.setRuleresult("模拟规则匹配失败,原因:" + ex.getMessage());
        }

        return tmpRespSimulateRule;
    }
}